﻿// =================================================================
//  Copyright (c) "David R. Guindo", Software architect
//  
//  This code is released under the terms of the Apache License, Version 2.0,
//  http://www.apache.org/licenses/LICENSE-2.0
//  
//  THIS CODE AND INFORMATION ARE PROVIDED WITHOUT WARRANTIES,
//  WITHOUT LIMITING COMMERCIAL OR PARTICULAR USE.
// =================================================================

namespace ScreenManager
{
   using System;
   using System.Collections.Generic;
   using System.Configuration;
   using System.Linq;
   using System.Windows;
   using System.Windows.Controls;
   using System.Windows.Forms;

   using global::ScreenManager.Configuration;

   using Application = System.Windows.Application;
   using Panel = System.Windows.Controls.Panel;

   public static partial class ScreenManager
   {

      private static readonly Dictionary<string, FrameworkElement> Containers =
         new Dictionary<string, FrameworkElement>();

      private static readonly Dictionary<string, object> Windows = new Dictionary<string, object>();

      private static ScreenManagerSection currentConfiguration;

      private static bool isInizialized;

      /// <summary>
      ///    Gets the channel container inside a Window.
      /// </summary>
      /// <param name="channel"> The channel name. </param>
      /// <param name="parameters"> The parameters needed into the windo constructor (if is needed). </param>
      /// <returns> The FrameworkElement container. </returns>
      public static FrameworkElement GetChannelContainer(string channel, params object[] parameters)
      {
         if (!isInizialized)
         {
            Inicialize();
         }
         if (Containers.ContainsKey(channel))
         {
            return Containers[channel];
         }
         SMWindow win = GetWindowContainer(channel);

         if (win != null)
         {
            //search in current app windows
            Window wininstance = Application.Current.Windows.OfType<Window>().FirstOrDefault(w => w.Name == win.Name);

            if (wininstance == null)
            {
               //create windows instance
               Type wintype = Type.GetType(win.Type);
               if (wintype == null)
               {
                  throw new Exception(
                     string.Format(
                        "Error in ScreenManager creating intance of {0} for window '{1}'." + Environment.NewLine
                        + " Configuration line number: {2}, config file: {3}",
                        win.Type,
                        win.Name,
                        win.ElementInformation.LineNumber,
                        win.ElementInformation.Source));
               }
               wininstance = (Window)Activator.CreateInstance(wintype, parameters);
               wininstance.Name = win.Name;

               var winContainer = GetWindowContainer(channel);
               Screen screen = GetScreen(winContainer);

               wininstance.Left = screen.WorkingArea.Left;
               wininstance.Top = screen.WorkingArea.Top;
               wininstance.Width = screen.WorkingArea.Width;
               wininstance.Height = screen.WorkingArea.Height;
               var winState = winContainer.WindowState;
               wininstance.WindowState = WindowState.Minimized;

               wininstance.Loaded += (s, e) =>
                  {
                     var window = s as Window;
                     if (window != null)
                     {
                        if (winState != WindowState.Minimized)
                        {
                           window.WindowState = WindowState.Maximized;
                           window.WindowState = winState;
                        }
                     }
                  };
               wininstance.Show();
            }

            if (!Windows.ContainsKey(win.Name))
            {
               Windows.Add(win.Name, wininstance);
            }

            win.ContainerViews.OfType<ContainerView>().ToList().ForEach(
               c =>
                  {
                     if (!Containers.ContainsKey(c.Name))
                     {
                        Containers.Add(c.Name, GetContainer(wininstance, c.ContainerName));
                     }
                  });

            if (Containers.ContainsKey(channel))
            {
               return Containers[channel];
            }
         }

         return null;
      }
     
      /// <summary>
      ///    Finds the container.
      /// </summary>
      /// <param name="uIElement"> The u I element. </param>
      /// <param name="containerName"> Name of the container. </param>
      /// <returns> </returns>
      private static FrameworkElement FindContainer(FrameworkElement uIElement, string containerName)
      {
         FrameworkElement container;

         if (uIElement is Decorator) //Border
         {
            if ((uIElement as Decorator).Child is FrameworkElement)
            {
               container = (uIElement as Decorator).Child as FrameworkElement;
               if (container != null && container.Name == containerName)
               {
                  return container;
               }
               return FindContainer(uIElement, containerName);
            }
         }
         else //grid, StackPanel, ...
         {
            var panel = uIElement as Panel;
            if (panel != null)
            {
               if (panel.Children != null)
               {
                  foreach (FrameworkElement f in panel.Children)
                  {
                     if (f.Name == containerName)
                     {
                        return f;
                     }
                     container = FindContainer(f, containerName);
                     if (container != null)
                     {
                        return container;
                     }
                  }
               }
            }
         }

         return null;
      }

      private static FrameworkElement GetContainer(object wininstance, string containerName)
      {
         if (wininstance is Window)
         {
            var fe = ((wininstance as Window).Content as FrameworkElement);
            if (fe != null && fe.Name == containerName)
            {
               return fe;
            }
            return FindContainer(fe, containerName);
         }

         return null;
      }

      /// <summary>
      ///    Gets the screen.
      /// </summary>
      /// <param name="window"> The window. </param>
      /// <returns> </returns>
      private static Screen GetScreen(SMWindow window)
      {
         int id;

         if (int.TryParse(window.Screen, out id))
         {
            return Screen.AllScreens[id];
         }

         //if (window.Screen.ToLowerInvariant()=="main")
         //   return System.Windows.Forms.Screen.PrimaryScreen;

         return Screen.PrimaryScreen;
      }

      private static SMWindow GetWindowContainer(string channel)
      {
         return (from SMWindow w in currentConfiguration.Windows
                 from ContainerView cv in w.ContainerViews
                 where cv.Name == channel
                 select w).FirstOrDefault();
      }

      private static void Inicialize()
      {
         currentConfiguration = (ScreenManagerSection)ConfigurationManager.GetSection("screenManagerSection");
         isInizialized = true;
      }
   }
}